#!/usr/bin/env python
"""
Acorn-SDR (A Collaboration of Radio Nodes)
(c) Bob Cowdery (G3UKB) 2009

Date: July 2009

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

The author can be reached by email at

bob@g3ukb.co.uk

========================================================================
Module Description:

A displays implementation using GTK+.

"""

#-----------------------------------------------------------------------
# System imports
import sys
from time import sleep
import subprocess
import string
from scipy import *
from scipy.signal.signaltools import resample

# Import GTK libraries
try:
  import pygtk
  #tell pyGTK, if possible, that we want GTKv2
  pygtk.require("2.0")
except:
  #Some distributions come with GTK2, but not pyGTK
  pass
try:
  import gtk
  import gtk.glade
  import gobject
  import pango
except:
  print "You need to install pyGTK or GTKv2 ",
  print "or set your PYTHONPATH correctly."
  print "try: export PYTHONPATH=",
  print "/usr/local/lib/python2.2/site-packages/"
  sys.exit(1)

# Application imports
import utils
from dttsp_map import *

#======================================================================
# The GUI class. There will be other classes downstream.
class AppDisplays:
  
    #---------------------------------------------------------------------
    def __init__(self, main_tree, persist_service, hw_pub, dsp_pub, upd_pub, ice_config, correction, rate, frames,  init):
        # Allow threads, without this the gui will hang all python threads
        # We don't actually use any Python threads right now but its very
        # disconcerting if you do and find they block, so leave this in
        gtk.gdk.threads_init()
        # Stash parameters
        self.main_tree = main_tree
        self.persist = persist_service
        self.hw_pub = hw_pub
        self.dsp_pub = dsp_pub
        self.upd_pub = upd_pub
        self.ice_config = ice_config
        self.correction = correction
        self.BANDWIDTH = float(rate)/1000000.0
        self.FRAMES = frames
        self.vr_data = init
        # Initialise variables
        self.pixmap = None
        self.pan_data = None
        self.timeline = None
        self.timeindex = 0
        self.AV_MAX = 10
        self.pwrlookup = []
        self.track_mouse = False
        self.f_caption = None
        self.sub_radios = [False, False, False, False]
        self.start_freq = None
        self.pan_value = 0
        self.zoom_value = 1
        # ToDo store this value
        self.average = True
        # Not reconfiguring
        self.config = False
        # ToDo configure these
        self.LOW_SCALE = -160
        self.HIGH_SCALE = -40
        # Set the initial data
        listen = self.vr_data['listen']
        if self.vr_data[0]['track'] == 'vfoa' or self.vr_data[0]['track'] == 'offseta':
            self.freq = self.vr_data[0]['vfoa']
            self.offset = self.vr_data[listen]['offseta']
            self.mode = self.vr_data[listen]['modea']
            self.filter = self.vr_data[listen]['filtera']
        else:
            self.freq = self.vr_data[0]['vfob']
            self.offset = self.vr_data[self.vr_data['listen']]['offsetb']
            self.mode = self.vr_data[listen]['modeb']
            self.filter = self.vr_data[listen]['filterb']
        
        # Wire up the top level window events
        # All events are set up in Glade, just match the event names here
        dic = { "on_Displays_destroy" : self.destroy,
                "on_Displays_destroy_event" : self.delete_event,
                "on_da_pan_configure_event" : self.config_event,
                "on_da_pan_expose_event" : self.expose_event,
                "on_da_pan_enter_notify_event": self.enter_event,
                "on_da_pan_leave_notify_event": self.leave_event,
                "on_da_pan_motion_notify_event": self.motion_event,
                "on_da_pan_button_press_event": self.button_press_event,
                "on_tb_average_toggled" : self.average_event,
                "on_hs_pan_value_changed": self.pan,
                "on_b_center_pan_clicked": self.pan_centre,
                "on_hs_zoom_value_changed": self.zoom,
                "on_b_center_zoom_clicked": self.zoom_centre,
            }
        
        # This will connect up all the events in one hit
        self.main_tree.signal_autoconnect (dic)
        
        # Now we need a reference to all the controls
        # All controls in the tree must have a unique name
        self.dapan = self.main_tree.get_widget('da_pan')
        self.tbaverage = self.main_tree.get_widget('tb_average')
        self.hszoom = self.main_tree.get_widget('hs_zoom')
        self.hspan = self.main_tree.get_widget('hs_pan')
        self.w = main_tree.get_widget('Displays')
        
        # Create the initial pixmap (as contrary to popular belief config_event does
        # not get called when the window is created)
        x, y, width, height = self.dapan.get_allocation()
        self.pixmap = gtk.gdk.Pixmap(self.dapan.window, width, height)
        # stipple pattern for transparency
        self.stipple = gtk.gdk.bitmap_create_from_data(None, '\x00\x01', 2, 2)
        # Pango layout for text
        self.layout = self.dapan.create_pango_layout("")        
        self.layout.set_width(25 * pango.SCALE)
        attrList = pango.AttrList()
        attribute = pango.AttrForeground(65535, 42075, 0, 0, -1)
        attrList.insert(attribute)
        self.layout.set_attributes(attrList)
        
        # Set up data
        self.init_data(width, height)
        
        # Create gc's for drawing
        colormap = self.dapan.get_colormap()
        green = colormap.alloc_color('green', True, True)
        self.drawing_gc = self.pixmap.new_gc(foreground=green, line_width=1)
        gray = colormap.alloc_color('DarkSlateGray', True, True)
        self.grid_gc = self.pixmap.new_gc(foreground=gray, line_width=1)
        red = colormap.alloc_color('red', True, True)
        self.axis_gc = self.pixmap.new_gc(foreground=red, line_width=1)
        # Filter blocks
        PaleGreen1 = colormap.alloc_color('PaleGreen1', True, True)
        PaleVioletRed1 = colormap.alloc_color('PaleVioletRed1', True, True)
        PaleTurquoise = colormap.alloc_color('PaleTurquoise', True, True)
        PaleGoldenrod = colormap.alloc_color('PaleGoldenrod', True, True)
        self.main_gc = self.pixmap.new_gc(foreground=PaleGreen1, fill=gtk.gdk.STIPPLED, stipple=self.stipple)
        self.rx1_gc = self.pixmap.new_gc(foreground=PaleVioletRed1, fill=gtk.gdk.STIPPLED, stipple=self.stipple)
        self.rx2_gc = self.pixmap.new_gc(foreground=PaleTurquoise, fill=gtk.gdk.STIPPLED, stipple=self.stipple)
        self.rx3_gc = self.pixmap.new_gc(foreground=PaleGoldenrod, fill=gtk.gdk.STIPPLED, stipple=self.stipple)
        self.filter_gc = [self.main_gc,self.rx1_gc,self.rx2_gc,self.rx3_gc]
        
        # Avoid circular events
        # One issue with messaging is that a lot of controls generate an event when they
        # are changed programmatically. So you hit a button, it sends a message, then a 
        # message comes back that causes the button state to change which causes it to 
        # generate an event ... 
        self.ignore = False
        
        # Set default state
        self.tbaverage.set_active(True)
        self.hspan.set_sensitive(False) # Not implemented
        
        # Finally, we need to set our GUI according to capabilities
        cap_multirx = utils.persist_get(self.persist, 'Capability', 'multirx')
        cap_vfo = utils.persist_get(self.persist, 'Capability', 'vfo')
        cap_oscillator = utils.persist_get(self.persist, 'Capability', 'oscillator')
        cap_tx = utils.persist_get(self.persist, 'Capability', 'tx')
        cap_graphics = utils.persist_get(self.persist, 'Capability', 'graphics')
        cap_displays = utils.persist_get(self.persist, 'Capability', 'displays')
    
        if not cap_graphics:
            print 'Sorry, displays are not supported with this profile!'
            gtk.main_quit()
            
        # When we have more displays cap_display will be used to select which we can show
        
    #---------------------------------------------------------------------
    # GUI generated events (i.e. things that happen when you play with widgets)
    
    #=====================================================================
    # Events for the main window
    def delete_event(self, widget, event, data=None):
        # If you return FALSE in the "delete_event" signal handler,
        # GTK will emit the "destroy" signal. Returning TRUE means
        # you don't want the window to be destroyed.
        # This is useful for popping up 'are you sure you want to quit?'
        # type dialogs.
    
        # Change FALSE to TRUE and the main window will not be destroyed
        # with a "delete_event".
        return False

    #---------------------------------------------------------------------
    def destroy(self, widget, data=None):
        gtk.main_quit()
    
    #=====================================================================
    # Events for the drawing area
    #---------------------------------------------------------------------
    # Called on window resize
    # Create a pixmap for the current window size
    # This seems to always be followed by an expose event, so let that do the drawing
    def config_event(self, widget, event):
        self.config = True
        x, y, width, height = widget.get_allocation()
        self.pixmap = gtk.gdk.Pixmap(widget.window, width, height)
        self.clear_pixmap(widget, width, height)
        self.init_data(width, height)
        self.config = False

    #---------------------------------------------------------------------
    # Called when the window needs redrawing
    # Can be forced by calling queue_draw()
    # Redraw the screen from the backing pixmap
    def expose_event(self, widget, event):
        if self.pixmap:
            x , y, width, height = event.area
            #x, y, width, height = self.dapan.get_allocation()
            self.clear_pixmap(widget, width, height)
            self.draw_static(widget)
            if self.pan_data:
                self.draw_dynamic(self.pan_data)
            # Refresh the display
            widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], self.pixmap, x, y, x, y, width, height)
        return False

    #---------------------------------------------------------------------
    def enter_event(self, widget, event):
        self.w.present()
        self.track_mouse = True
        
    #---------------------------------------------------------------------
    def leave_event(self, widget, event):
        self.track_mouse = False
        self.f_caption = None
        
    #---------------------------------------------------------------------
    def motion_event(self, widget, event):
        if self.track_mouse and self.start_freq:
            pos = (event.x-self.xinc)
            if pos >=0:
                self.f_caption = (int(event.x), int(event.y), self.start_freq + ((event.x-self.xinc)*self.freq_per_pixel))
            else:
                self.f_caption = None
        else:
            self.f_caption = None
     
    #---------------------------------------------------------------------
    def button_press_event(self, widget, event):
        if self.f_caption <> None:
            self.hw_pub.freqSet(self.f_caption[2] - self.offset)
        
    #=====================================================================
    # Other interactors
    #---------------------------------------------------------------------
    def average_event(self, widget):
        if not self.ignore:
            if widget.get_active():
                self.average = True
                self.tbaverage.set_label('Average')
            else:
                self.average = False
                self.tbaverage.set_label('Peak')
    
    #--------------------------------------------------------------------- 
    def pan(self, widget):
        self.pan_value = widget.get_value()
        
    #--------------------------------------------------------------------- 
    def pan_centre(self, widget):
        self.pan_value = 0
        
    #--------------------------------------------------------------------- 
    def zoom(self, widget):
        if not self.ignore:
            self.zoom_value = widget.get_value()
            x, y, width, height = self.dapan.get_allocation()
            self.xinc = int(float(width)/9.0)
            # Calculate the frequency increment per pixel
            self.freq_per_pixel = ((self.BANDWIDTH/self.zoom_value)*2)/float(width-self.xinc)
        
    #--------------------------------------------------------------------- 
    def zoom_centre(self, widget):
        self.ignore = True
        self.zoom_value = 1
        self.hszoom.set_value(1)
        x, y, width, height = self.dapan.get_allocation()
        self.xinc = int(float(width)/9.0)
        # Calculate the frequency increment per pixel
        self.freq_per_pixel = ((self.BANDWIDTH/self.zoom_value)*2)/float(width-self.xinc)
        self.ignore = False
        
    #---------------------------------------------------------------------
    # Interface
    #---------------------------------------------------------------------
    #---------------------------------------------------------------------
    # Panadapter data event
    def thd_pan(self, pan_data):
        gobject.idle_add(self.pan, pan_data)
        
    def pan(self, pan_data):
        x, y, width, height = self.dapan.get_allocation()
        self.pan_data = pan_data
        self.clear_pixmap(self.dapan, width, height)
        self.draw_static(self.dapan)
        self.draw_dynamic(pan_data)
        # Request a repaint
        self.dapan.queue_draw()
  
    #---------------------------------------------------------------------
    def thd_freq_update(self, freq):
        if self.vr_data[0]['track'] == 'vfoa' or self.vr_data[0]['track'] == 'offseta':
            self.vr_data[0]['vfoa'] = freq 
        else:
            self.vr_data[0]['vfob'] = freq 
        self.freq = freq
        #gobject.idle_add(self.freq_update)
        
    def freq_update(self):
        x, y, width, height = self.dapan.get_allocation()
        self.clear_pixmap(self.dapan, width, height)
        self.draw_static(self.dapan)
        self.dapan.queue_draw()
    
    #---------------------------------------------------------------------
    def thd_mode_update(self, mode):
        listen = self.vr_data['listen']
        if listen == 0:
            if self.vr_data[0]['track'] == 'vfoa' or self.vr_data[0]['track'] == 'offseta':
                self.vr_data[listen]['modea'] = mode
            else:
                self.vr_data[listen]['modeb'] = mode
        else:
            self.vr_data[listen]['mode'] = mode
        
    #---------------------------------------------------------------------
    def thd_filter_update(self, filter):
        listen = self.vr_data['listen']
        if listen == 0:
            if self.vr_data[0]['track'] == 'vfoa' or self.vr_data[0]['track'] == 'offseta':
                self.vr_data[listen]['filtera'] = filter
            else:
                self.vr_data[listen]['filterb'] = filter
        else:
            self.vr_data[listen]['filter'] = filter
        
    #---------------------------------------------------------------------
    def thd_offset_update(self, freq):
        listen = self.vr_data['listen']
        if self.vr_data[0]['track'] == 'vfoa' or self.vr_data[0]['track'] == 'offseta':
            self.vr_data[listen]['offseta'] = freq 
        else:
            self.vr_data[listen]['offsetb'] = freq
        self.offset = freq
     
    # The track and listen updates can change the centre frequency as the offset
    # is swapped to the correct vfo and/or receiver
    #---------------------------------------------------------------------
    def thd_listen_update(self, rx):
        self.vr_data['listen'] = rx
        if self.vr_data[0]['track'] == 'vfoa' or self.vr_data[0]['track'] == 'offseta':
            self.offset = self.vr_data[rx]['offseta']
        else:
            self.offset = self.vr_data[rx]['offsetb']
    
    #---------------------------------------------------------------------
    def thd_track_update(self, track):
        self.vr_data[0]['track'] = track
        if track == 'vfoa' or track == 'offseta':
            self.freq = self.vr_data[0]['vfoa']
            self.offset = self.vr_data[0]['offseta']
        else:
            self.freq = self.vr_data[0]['vfob']
            self.offset = self.vr_data[0]['offsetb']
     
    #---------------------------------------------------------------------
    def thd_rx_on_update(self, rx):
        self.sub_radios[rx] = True
    
    #---------------------------------------------------------------------
    def thd_rx_off_update(self, rx):
        self.sub_radios[rx] = False
        
    #---------------------------------------------------------------------
    def thd_terminate(self):
        gobject.idle_add(self.terminate)
    
    #---------------------------------------------------------------------
    def terminate(self):
        gtk.main_quit()
     
    #-----------------------------------------------------------------------
    # Explicitly called config handlers
    # We need to handle these carefully else the audio will get messed up.
    #-----------------------------------------------------------------------
    def set_audiorate(self, value):
        self.BANDWIDTH = float(value)/1000000.0
        x, y, width, height = self.dapan.get_allocation()
        self.init_data(width, height)
        
    #-----------------------------------------------------------------------
    def set_audioframes(self, value):
        self.RATE = value
        x, y, width, height = self.dapan.get_allocation()
        self.init_data(width, height)
        
    #-----------------------------------------------------------------------
    def set_level(self, value):
        self.correction = value
        
    #=====================================================================
    # Private
    #---------------------------------------------------------------------
    def init_data(self, width, height):
        # Create the timeline buffer for averaging
        # Note we only average the resampled array - this may not give best resolution!
        self.xinc = int(float(width)/9.0)
        self.timeline = zeros((self.AV_MAX,width-self.xinc),'f')
        self.timeindex = 0
        # Create the power lookup table
        self.yinc = int(float(height)/8.0)
        pwr_range = abs(self.LOW_SCALE) - abs(self.HIGH_SCALE)
        inc = float(height-self.yinc)/float(pwr_range)
        self.pwrlookup = []
        for i in range(pwr_range):
            self.pwrlookup.append(int(round(float(i)*inc,1) + self.yinc))
        # Calculate the frequency increment per pixel
        self.freq_per_pixel = ((self.BANDWIDTH/self.zoom_value)*2)/float(width-self.xinc)
     
    #---------------------------------------------------------------------       
    def draw_static(self, widget):
        if self.config: return
        
        # Adjust for zoom
        bandwidth = self.BANDWIDTH/self.zoom_value
        
        y_char_offset = -9
        x_char_offset = -35
        self.start_freq = round(self.freq + self.offset - bandwidth, 3)
        finc = round((bandwidth*2)/8.0, 4)
        x , y, width, height = self.dapan.get_allocation()
        # Coords for y axis
        yinc = float(height)/7.0
        self.yinc = int(float(height)/7.0)
        yaxis = [
                ('-40',(10,int(yinc))),
                ('-60',(10,int(yinc*2.0))),
                ('-80',(10,int(yinc*3.0))),
                ('-100',(10,int(yinc*4.0))),
                ('-120',(10,int(yinc*5.0))),
                ('-140',(10,int(yinc*6.0))),                   
                ]
        # Coords for x axis
        xinc = float(width)/9.0
        self.xinc = int(float(width)/9.0)
        xaxis = [
                (str(self.start_freq),(int(xinc),5)),
                (str(self.start_freq+finc),(int(xinc*2.0),5)),
                (str(self.start_freq+finc*2.0),(int(xinc*3.0),5)),
                (str(self.start_freq+finc*3.0),(int(xinc*4.0),5)),
                (str(self.start_freq+finc*4.0),(int(xinc*5.0),5)),
                (str(self.start_freq+finc*5.0),(int(xinc*6.0),5)),
                (str(self.start_freq+finc*6.0),(int(xinc*7.0),5)),
                (str(self.start_freq+finc*7.0),(int(xinc*8.0),5)),
                (str(self.start_freq+finc*8.0),(int(xinc*9.0),5)),
                ]
        # Draw static data
        for item in yaxis:
            self.pixmap.draw_line(self.grid_gc, 40, item[1][1], width, item[1][1])
            self.layout.set_alignment(pango.ALIGN_RIGHT)
            self.layout.set_text(item[0])
            self.pixmap.draw_layout(widget.get_style().fg_gc[gtk.STATE_NORMAL], item[1][0], item[1][1]+y_char_offset, self.layout)
        item_count = 0
        for item in xaxis:
            if item_count == 4:
                gc = self.axis_gc
            else:
                gc = self.grid_gc
            self.pixmap.draw_line(gc, item[1][0], 20, item[1][0], height, )
            self.layout.set_alignment(pango.ALIGN_LEFT)
            self.layout.set_text(item[0])
            self.pixmap.draw_layout(widget.get_style().fg_gc[gtk.STATE_NORMAL], item[1][0]+x_char_offset, item[1][1], self.layout)
            item_count += 1
        
        # Display the frequency at the cursor
        if self.f_caption <> None:
            self.layout.set_text(str(round(self.f_caption[2], 6)))
            if self.f_caption[0] > (width-80):
                xcoord = self.f_caption[0] - 50
            else:
                xcoord = self.f_caption[0] + 20
            if self.f_caption[1] > (height-20):
                ycoord = self.f_caption[1] - 15
            else:
                ycoord = self.f_caption[1]
            self.pixmap.draw_layout(widget.get_style().fg_gc[gtk.STATE_NORMAL], xcoord, ycoord, self.layout)
            
    #---------------------------------------------------------------------
    def draw_dynamic(self, pan_data):
        if self.config: return
        
        # Get the drawing area properties
        x, y, width, height = self.dapan.get_allocation()
        
        # Adjust for zoom
        sz = len(pan_data)
        half_sz = sz/2.0
        # Slice out the part of the data we are looking at
        pan_data = pan_data[int(half_sz)-int(half_sz/self.zoom_value):int(half_sz)+int(half_sz/self.zoom_value)]

        # We have an array of absolute values which are in dbM
        # Resample to the display width
        timeline = asarray(pan_data, 'f')
        timeline = resample(timeline, width-self.xinc)
        timeline = timeline + self.correction
        
        if self.average:
        # Average the display over AV_MAX iterations        
            if self.timeindex == self.AV_MAX-1:
                self.timeindex = 0
            else:
                self.timeindex += 1
            self.timeline[self.timeindex] = timeline
            timeline = self.timeline.sum(axis=0)/self.AV_MAX
        
        # Plot in integers
        timeline = timeline.astype('i')
        
        # Now we have the averaged 1D array of dbM values
        # Now create a list of tuples for each point
        # This creates a 2D array with the first axis 0...width and the second data
        plot = array([range(self.xinc, width), timeline])
        # Creates a new array of shape (width,2) with corresponding elements of both axis in each array
        # i.e. a list of x,y plot points
        plot = plot.transpose()
        # draw_lines wants a list of tuples not a 2D array
        # also converts the power to a y point
        #print plot
        replot = []
        for item in plot:
            # Convert the power to a point on the y axis
            try:
                item[1] = self.pwrlookup[abs(item[1])+self.HIGH_SCALE]
            except:
                pass
                #print 'Item is out of range ',item[1], abs(item[1])+self.HIGH_SCALE
            item = tuple(item)
            replot.append(item)
        #print replot
        # Plot the points in one call
        self.pixmap.draw_lines(self.drawing_gc, replot)
            
        # Draw the appropriate filter blocks
        listen = self.vr_data['listen']
        track = ''
        if self.vr_data[0]['track'] == 'vfoa' or self.vr_data[0]['track'] == 'offseta':
            track = 'a'
            mode_0 = self.vr_data[0]['modea']
            filter_0 = self.vr_data[0]['filtera']
            offset_0 = self.offset + self.vr_data[0]['offseta']
            offset_n = self.offset + self.vr_data[listen]['offseta']
        else:
            track = 'b'
            mode_0 = self.vr_data[0]['modeb']
            filter_0 = self.vr_data[0]['filterb']
            offset_0 = self.offset + self.vr_data[0]['offsetb']
            offset_n = self.offset + self.vr_data[listen]['offsetb']

        offset_nil = 0.0
        
        # Do the listening receiver, this is centered on the display
        #print 'Listen: ', listen, ' Radios: ', self.sub_radios
        if listen == 0:
            # Draw the main receiver
           self.draw_filter_block(self.dapan, self.filter_gc[0], 0, mode_0, filter_0, offset_nil, width, height)
        else:
            # Draw the listening sub-receiver
            self.draw_filter_block(self.dapan, self.filter_gc[listen], listen, self.vr_data[listen]['mode'], self.vr_data[listen]['filter'], offset_nil, width, height)
            # and the main receiver offset to its correct frequency
            if track == 'a':
                offset = self.vr_data[0]['offseta'] - self.vr_data[listen]['offseta']
            else:
                offset = self.vr_data[0]['offsetb'] - self.vr_data[listen]['offsetb']
            self.draw_filter_block(self.dapan, self.filter_gc[0], 0, mode_0, filter_0, offset, width, height)
        
        
        # For the other active receivers draw where they are listening
        for n in range(1,3):
            # Active sub-receivers
            if self.sub_radios[n] == True:
                if n <> listen:
                    # Not already done
                    if track == 'a':
                        offset = self.vr_data[n]['offseta'] - self.vr_data[listen]['offseta']
                    else:
                        offset = self.vr_data[n]['offsetb'] - self.vr_data[listen]['offsetb']
                        
                    self.draw_filter_block(self.dapan, self.filter_gc[n], n, self.vr_data[n]['mode'], self.vr_data[n]['filter'], offset, width, height)
        
    #---------------------------------------------------------------------
    def clear_pixmap(self, widget, width, height):
        self.pixmap.draw_rectangle(widget.get_style().black_gc, True, 0, 0, width, height)
    
    #--------------------------------------------------------------------- 
    def draw_filter_block(self, widget, gc, radio, mode, filter, offset, width, height):
        filter_def = filter_map[filter]
        # Draw a filled rectangle with alpha channel
        # ToDo better than 1 bit alpha, how does one do proper alpha blend
        # Find y coordinates
        low_pix_offset = int((float(filter_def[0])/1000000.0)/self.freq_per_pixel)
        high_pix_offset = int((float(filter_def[1])/1000000.0)/self.freq_per_pixel)
        rx_offset = int(offset/self.freq_per_pixel)
        if mode <> None:
            if mode == 'LSB' or mode == 'CW-L':
                x = (width+self.xinc)/2-high_pix_offset+rx_offset
                self.pixmap.draw_rectangle(gc, True, x, self.yinc, high_pix_offset-low_pix_offset, height-self.yinc)
            elif mode == 'USB' or mode == 'CW-U':
                x = (width+self.xinc)/2+low_pix_offset+rx_offset
                self.pixmap.draw_rectangle(gc, True, x, self.yinc, high_pix_offset-low_pix_offset, height-self.yinc)
            else:
                x = (width+self.xinc)/2-high_pix_offset+rx_offset
                self.pixmap.draw_rectangle(gc, True, x, self.yinc, high_pix_offset*2, height-self.yinc)
        
        # Label the radio
        self.layout.set_text(str(radio))
        self.pixmap.draw_layout(widget.get_style().fg_gc[gtk.STATE_NORMAL], x+2, height-15, self.layout)
        
    #=====================================================================
    # Entry point
    def main(self):
        # All PyGTK applications must have a gtk.main(). Control ends here
        # and waits for an event to occur (like a key press or mouse event)
        gtk.main()
